Skip to content
标签
负载均衡
字数
2100 字
阅读时间
9 分钟

一、概述

是 Netflixfa 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。在 SpringCloud 中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。

在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务

1.1Rabbon负载均衡流程图

image-20201115221503574

1、在消费微服务中使用Ribbon实现负载均衡,Ribbon先从EurekaServer中获取服务列表。

2、Ribbon根据负载均衡的算法去调用微服务。

1.2 基本组件

ServerList:可以响应客户端的特定服务的服务器列表。

ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。

ServerListUpdater:用于执行动态服务器列表更新。

Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。

Ping:客户端用于快速检查服务器当时是否处于活动状态。

LoadBalancer:负载均衡器,负责负载均衡调度的管理。

1.3 主要作用

(1)服务调用

基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助RestTemplate 最终进行调用

(2)负载均衡

当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址

1.4 流程分析

LoadBalancerInterceptor,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。

通过intercept方法,拦截了用户的HttpRequest请求

  • request.getURI():获取请求uri,
  • originalUri.getHost():获取uri路径的主机名,其实就是服务id,
  • this.loadBalancer.execute():处理服务id,和用户请求。

this.loadBalancerLoadBalancerClient类型。

调用LoadBalancerClient.execute()方法。

  • getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而ILoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
  • getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。

通过chooseServer中的rule.choose()发放选择负载均衡规则。默认为轮询。

Ribbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:

基本流程如下:

  • 拦截我们的RestTemplate请求http://userservice/user/1
  • RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
  • DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
  • eureka返回列表,localhost:8081、localhost:8082
  • IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
  • RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求

1.5 负载均衡策略

负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:

不同规则的含义如下:

内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit属性进行配置。
WeightedResponseTimeRule为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。
BestAvailableRule忽略那些短路的服务器,并选择并发数较低的服务器。
RandomRule随机选择一个可用的服务器。
RetryRule重试机制的选择逻辑

默认的实现就是ZoneAvoidanceRule,是一种轮询方案

二、应用示例

2.1 与springcloud集成

依赖

xml
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring‐cloud‐starter‐ribbon</artifactId>
</dependency>
<dependency>
	<groupId>com.squareup.okhttp3</groupId>
	<artifactId>okhttp</artifactId>
</dependency>

配置

yml
ribbon:
	MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试
	MaxAutoRetriesNextServer: 3 #切换实例的重试次数
	OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
	ConnectTimeout: 5000 #请求连接的超时时间
	ReadTimeout: 6000 #请求处理的超时时间

三、核心知识

3.1 自定义负载均衡策略

通过定义IRule实现可以修改负载均衡规则,有两种方式:

  1. 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
java
@Bean
public IRule randomRule(){
    return new RandomRule();
}
  1. 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
yaml
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

注意,一般用默认的负载均衡规则,不做修改。

3.2 饥饿加载

Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

yaml
ribbon:
  eager-load:
    enabled: true
    clients: userservice

四、源码分析

@LoadBalanced使用Ribbon完成客户端负载均衡

@LoadBalanced注解是用来给RestTemplate做标记,方便我们对RestTemplate添加一个LoadBalancerClient,以实现客户端负载均衡。

自动装配

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

通过 RibbonAutoConfiguration 引入了 LoadBalancerAutoConfiguration 配置类

LoadBalancerAutoConfiguration中主要做了下面三件事:

创建了一个 LoadBalancerInterceptor 的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。

创建了一个 RestTemplateCustomizer 的Bean,用于给 RestTemplate 增加LoadBalancerInterceptor 拦截器。

维护了一个被 @LoadBalanced 注解修饰的 RestTemplate 对象列表,并在这里进行初始化,通过调用 RestTemplateCustomizer 的实例来给需要客户端负载均衡的 RestTemplate 增加LoadBalancerInterceptor 拦截器。

LoadBalancerInterceptor中注入了 LoadBalancerClient 的实现。当一个被 @LoadBalanced 注解修饰的 RestTemplate 对象向外发起HTTP请求时,会被LoadBalancerInterceptor 类的 intercept 函数所拦截。由于我们在使用RestTemplate时候采用了服务名作为host,所以直接从 HttpRequest 的URI对象中通过getHost()就可以拿到服务名,然后调用execute 函数去根据服务名来选择实例并发起实际的请求。

LoadBalancerClient 只是一个抽象的负载均衡器接口,实现类:RibbonLoadBalancerClient

实现类中方法:

ServiceInstance choose(String serviceId):根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。

T execute(String serviceId, LoadBalancerRequest request):根据传入的服务id,指定的负载均衡器中的服务实例执行请求。

T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request):根据传入的服务实例,执行请求。

实际负载均衡的是通过 ILoadBalancer 来实现的